package com.zmn.mcc.manager.utils;

import com.alibaba.fastjson.JSON;
import com.dingtalk.api.request.OapiSmartworkHrmEmployeeUpdateRequest;
import com.google.common.collect.Lists;
import com.zmn.common.utils.string.StringUtil;
import com.zmn.mcc.model.entity.staff.dingtalk.DingTalkField;
import com.zmn.mcc.model.entity.staff.DingTalkResult;
import com.zmn.mcc.model.entity.staff.dingtalk.StaffDingTalk;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.beanutils.ConvertUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Log4j2
public class DingTalkUtil {


    static final Pattern paramTemplate = Pattern.compile("\\$\\{(.*?)\\}");

    /**
     * 钉钉返回对象转换实体对象
     *
     * @param field_list
     * @param tClass
     * @param <T>
     * @return
     */
    public static <T> T dingTalkResultToObj(List<DingTalkResult> field_list, Class<T> tClass) {

        Map<String, Field> fieldMap = Arrays.stream(tClass.getDeclaredFields())
                .filter(item -> item.isAnnotationPresent(DingTalkField.class))
                .collect(Collectors.toMap(item -> item.getAnnotation(DingTalkField.class).value(), item -> item));

        T t = null;
        try {
            t = tClass.newInstance();
        } catch (InstantiationException e) {
            log.error(e);
        } catch (IllegalAccessException e) {
            log.error(e);
        }
        for (DingTalkResult resultField : field_list) {
            Field field = fieldMap.get(resultField.getField_code());

            if (field != null && Objects.nonNull(resultField.getValue())) {

                DingTalkField dingTalkFieldMapping = field.getAnnotation(DingTalkField.class);

                Object value = dingTalkFieldMapping.lable() ? resultField.getLabel() : resultField.getValue();
                try {
                    String convertMethodStr = dingTalkFieldMapping.convertMethod();
                    if (StringUtil.isNotBlank(convertMethodStr) && value != null) {
                        value = invokeConvertMethod(value, convertMethodStr);
                    }
                    String fieldName = field.getName();
                    String setMethodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    Method setMethod = t.getClass().getDeclaredMethod(setMethodName, field.getType());
                    setMethod.invoke(t, field.getType() == value.getClass() ? value : ConvertUtils.convert(value, field.getType()));
                } catch (NoSuchMethodException e) {
                    log.error(e);
                } catch (ClassNotFoundException e) {
                    log.error(e);
                } catch (InvocationTargetException e) {
                    log.error(e);
                } catch (IllegalAccessException e) {
                    log.error(e);
                }
            }

        }

        return t;
    }

    /**
     * 实体对象转换钉钉更新员工请求对象
     *
     * @param field_list
     * @param tClass
     * @param <T>
     * @return
     */
    public static List<OapiSmartworkHrmEmployeeUpdateRequest.GroupMetaInfo> objToDingTalkUpdateUserRequest(StaffDingTalk staffDingTalk) {
        List<OapiSmartworkHrmEmployeeUpdateRequest.GroupMetaInfo> groups = new ArrayList<>();

        Class<? extends StaffDingTalk> tClass = staffDingTalk.getClass();

        Map<String, List<Field>> groupFieldsMap = Arrays.stream(tClass.getDeclaredFields())
                .filter(item -> item.isAnnotationPresent(DingTalkField.class))
                .collect(Collectors.groupingBy(field -> field.getAnnotation(DingTalkField.class).groupId()));

        for (Map.Entry<String, List<Field>> entry : groupFieldsMap.entrySet()) {
            String groupId = entry.getKey();
            List<Field> groupFields = entry.getValue();
            OapiSmartworkHrmEmployeeUpdateRequest.GroupMetaInfo groupMetaInfo = new OapiSmartworkHrmEmployeeUpdateRequest.GroupMetaInfo();

            List<OapiSmartworkHrmEmployeeUpdateRequest.EmpListFieldVO> sections = groupFields.stream().map(field -> {
                DingTalkField dingTalkFieldMapping = field.getAnnotation(DingTalkField.class);
                OapiSmartworkHrmEmployeeUpdateRequest.EmpListFieldVO fieldVO = new OapiSmartworkHrmEmployeeUpdateRequest.EmpListFieldVO();
                OapiSmartworkHrmEmployeeUpdateRequest.EmpFieldVo empFieldVo = new OapiSmartworkHrmEmployeeUpdateRequest.EmpFieldVo();
                try {
                    empFieldVo.setFieldCode(dingTalkFieldMapping.value());

                    String fieldName = field.getName();
                    String getMethodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
                    Method getMethod = tClass.getDeclaredMethod(getMethodName);
                    Object value = getMethod.invoke(staffDingTalk);

                    if (value == null) {
                        return null;
                    }

                    String convertMethodStr = dingTalkFieldMapping.reverseConvertMethod();
                    if (StringUtil.isNotBlank(convertMethodStr) && value != null) {
                        value = invokeConvertMethod(value, convertMethodStr);
                    }

                    empFieldVo.setValue(value == null ? "" : String.valueOf(value));
                } catch (IllegalAccessException e) {
                    log.error(e);
                } catch (NoSuchMethodException e) {
                    log.error(e);
                } catch (InvocationTargetException e) {
                    log.error(e);
                } catch (ClassNotFoundException e) {
                    log.error(e);
                }
                fieldVO.setSection(Lists.newArrayList(empFieldVo));
                return fieldVO;
            }).filter(Objects::nonNull).collect(Collectors.toList());

            if (sections.size() > 0) {
                groupMetaInfo.setGroupId(groupId);
                groupMetaInfo.setSections(sections);
                groups.add(groupMetaInfo);
            }
        }

        return groups;
    }

    private static Object invokeConvertMethod(Object value, String convertMethodStr) throws ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        String[] classMethod = convertMethodStr.split("#");

        Class<?> aClass = Class.forName(classMethod[0]);

        if (classMethod[1].contains("(") && value != null) {
            String methodName = classMethod[1].substring(0, classMethod[1].indexOf("("));

            String paramsStr = classMethod[1]
                    .substring(classMethod[1].indexOf("(") + 1, classMethod[1].length() - 1);
//                    .replaceAll("\\$this", value == null ? "" : String.valueOf(value));

            // "${java.lang.String, $this},${java.lang.String, yyyy-MM-dd}"
            Matcher matcher = paramTemplate.matcher(paramsStr);
            int count = 0;
            while (matcher.find()) {
                count++;
            }
            Class<?>[] parameterTypes = new Class[count];
            Object[] parameterValues = new Object[count];

            matcher = paramTemplate.matcher(paramsStr);
            for (int i = 0; matcher.find(); i++) {
                String group = matcher.group();
                if (group.startsWith("${") && group.endsWith("}")) {
                    String[] classParams = group.substring(2, group.length() - 1).split(",");
                    Class paramClass = Class.forName(classParams[0].trim());
                    Object paramValue = null;
                    paramValue = classParams[1].trim().equals("$this") ? value : classParams[1].trim();
                    if (paramValue != null) {
                        paramValue = paramClass.getCanonicalName().equals("java.lang.String") ? paramValue : ConvertUtils.convert(paramValue, paramClass);
                    }

                    parameterTypes[i] = paramClass;
                    parameterValues[i] = paramValue;
                } else {
                    throw new IllegalAccessException("params template format is error! " + group);
                }
            }

            Method convertMethod = aClass.getMethod(methodName, parameterTypes);

            Object invoke = convertMethod.invoke(null, parameterValues);
            value = invoke;
        }
        return value;
    }


    public static void main(String[] args) {
        List<DingTalkResult> field_list = new ArrayList<>();
        field_list.add(new DingTalkResult("sys01", "sys01-employeeType", "全职", "全职", "员工类型"));
        field_list.add(new DingTalkResult("sys01", "sys01-regularTime", "2020-07-02", "2020-07-02", "转正时间"));

        StaffDingTalk staffDingTalk = dingTalkResultToObj(field_list, StaffDingTalk.class);
        System.out.println("钉钉getUser:");
        System.out.println(staffDingTalk);

        System.out.println();
        System.out.println();

        System.out.println("钉钉updateUser:");
        List<OapiSmartworkHrmEmployeeUpdateRequest.GroupMetaInfo> groupMetaInfos = objToDingTalkUpdateUserRequest(staffDingTalk);
        System.out.println(JSON.toJSONString(groupMetaInfos));


    }

}
